﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Reflection;
using System.Resources;

namespace DaveSexton.Labs
{
	/// <summary>
	/// Provides members that specify the activation and discovery strategy
	/// of the <see cref="LabController"/>, and optionally creates 
	/// <see cref="Lab"/> objects to be executed with priority before all
	/// of the discovered labs.
	/// </summary>
	[ContractClass(typeof(LabCatalogContract))]
	public abstract class LabCatalog
	{
		#region Public Properties
		/// <summary>
		/// Gets the strategy that the <see cref="LabController"/> will use to 
		/// discover and activate labs.
		/// </summary>
		public abstract LabActivationStrategies Activation
		{
			get;
		}

		/// <summary>
		/// Gets a sequence of types that derive from <see cref="Lab"/> to be 
		/// used by the <see cref="LabController"/> during automatic discovery.
		/// The specific semantics of <see cref="LabTypes"/> depends upon the 
		/// <see cref="Activation"/> strategy of the <see cref="LabCatalog"/>.
		/// </summary>
		public abstract IEnumerable<Type> LabTypes
		{
			get;
		}

		/// <summary>
		/// Gets a sequence of <see cref="ILab"/> objects to be activated or displayed by
		/// the <see cref="LabController"/> in the order that they are received.
		/// </summary>
		/// <remarks>
		/// <alert type="note">
		/// Whether or not <see cref="PriorityLabs"/> are used by the <see cref="LabController"/>
		/// depends upon the <see cref="Activation"/> strategy of the <see cref="LabCatalog"/>.
		/// </alert>
		/// </remarks>
		public abstract IEnumerable<ILab> PriorityLabs
		{
			get;
		}
		#endregion

		#region Private / Protected
		private static readonly object gate = new object();
		private static readonly Dictionary<Assembly, ResourceManager> sourceCodeResourceManagers = new Dictionary<Assembly, ResourceManager>();
		private static readonly Dictionary<Assembly, ResourceManager> sourceXamlResourceManagers = new Dictionary<Assembly, ResourceManager>();
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="LabCatalog" /> class for derived classes.
		/// </summary>
		protected LabCatalog()
		{
		}
		#endregion

		#region Methods
		internal ResourceManager GetSourceCodeResourceManager(Assembly assembly)
		{
			Contract.Requires(assembly != null);

			lock (gate)
			{
				if (sourceCodeResourceManagers.ContainsKey(assembly))
				{
					return sourceCodeResourceManagers[assembly];
				}
				else
				{
					var resourceManager = CreateSourceCodeResourceManager(assembly);

					sourceCodeResourceManagers.Add(assembly, resourceManager);

					return resourceManager;
				}
			}
		}

		internal ResourceManager GetSourceXamlResourceManager(Assembly assembly)
		{
			Contract.Requires(assembly != null);

			lock (gate)
			{
				if (sourceXamlResourceManagers.ContainsKey(assembly))
				{
					return sourceXamlResourceManagers[assembly];
				}
				else
				{
					var resourceManager = CreateSourceXamlResourceManager(assembly);

					sourceXamlResourceManagers.Add(assembly, resourceManager);

					return resourceManager;
				}
			}
		}

		protected virtual ResourceManager CreateSourceCodeResourceManager(Assembly assembly)
		{
			Contract.Requires(assembly != null);

			return new ResourceManager("LabSourceCode", assembly);
		}

		protected virtual ResourceManager CreateSourceXamlResourceManager(Assembly assembly)
		{
			Contract.Requires(assembly != null);

			return new ResourceManager("LabXaml", assembly);
		}
		#endregion
	}

	[ContractClassFor(typeof(LabCatalog))]
	public abstract class LabCatalogContract : LabCatalog
	{
		public override LabActivationStrategies Activation
		{
			get
			{
				return LabActivationStrategies.None;
			}
		}

		public override IEnumerable<Type> LabTypes
		{
			get
			{
				Contract.Ensures(Contract.Result<IEnumerable<Type>>() != null);
				return null;
			}
		}

		public override IEnumerable<ILab> PriorityLabs
		{
			get
			{
				Contract.Ensures(Contract.Result<IEnumerable<ILab>>() != null);
				return null;
			}
		}
	}
}